/*
* Implementing logs using threads and queues
*/

#include "../inc/log.h"
#include <pthread.h>
#include <stdlib.h>

Queue *q_log;
FILE * fw;
int stop = 0;
pthread_t log_thread;
pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond_full = PTHREAD_COND_INITIALIZER;

/* setting the stop flag of log task */
void set_stop(){
    stop = 1;
}

/* Log cache for each file */
int file_log(Queue *q, enum debug_type type, char* filename, const char* format, ...){
    if(type == get_debug()){
        char *buffer = (char*)malloc(sizeof(char) * 1050);
        va_list args;
        va_start(args, format);
        vsnprintf(buffer, sizeof(char) * 1050, format, args);
        va_end(args);
        char *message = (char*)malloc(sizeof(char) * 1100);
        snprintf(message, sizeof(char) * 1100, "%s: %s", filename, buffer);
        Pair *pair = (Pair*)malloc(sizeof(Pair));
        int* flag = (int*)malloc(sizeof(int));
        *flag = 0;
        pair->first = flag;
        pair->second = message;
        queue_push(q, pair);
        free(buffer);
    }
    return 0;
}

/* log of each file save to queue */
void file_queue_push(void *data){
    pthread_mutex_lock(&log_mutex);
    queue_push(q_log, data);
    pthread_cond_signal(&cond_full);
    pthread_mutex_unlock(&log_mutex);
}

/* ordinary logging except for file */
int _log(enum debug_type type, char* filename, const char* format, ...){
    if(type <= get_debug()){
        pthread_mutex_lock(&log_mutex);
        char *buffer = (char*)malloc(sizeof(char) * 1050);
        va_list args;
        va_start(args, format);
        vsnprintf(buffer, sizeof(char) * 1050, format, args);
        va_end(args);
        char *message = (char*)malloc(sizeof(char) * 1100);
        snprintf(message, sizeof(char) * 1100, "%s: %s", filename, buffer);
        Pair *pair = (Pair*)malloc(sizeof(Pair));
        int* flag = (int*)malloc(sizeof(int));
        *flag = 1;
        pair->first = flag;
        pair->second = message;
        queue_push(q_log, pair);
        pthread_cond_signal(&cond_full);
        pthread_mutex_unlock(&log_mutex);
        free(buffer);
    }
    return 0;
}

/* log queue wrtie to a file */
void* write_log(void* args){
    while(1){
        pthread_mutex_lock(&log_mutex);
        while(!stop && queue_size(q_log) == 0) {
            pthread_cond_wait(&cond_full, &log_mutex);
        }
        if(is_empty(q_log) && stop){
            pthread_mutex_unlock(&log_mutex);
            break;
        } 
        Pair *p = (Pair*)queue_pop(q_log);
        if(*(int*)p->first == 1){
            time_t now = time(NULL);
            struct tm *local_time = localtime(&now);
            char timestamp[30];
            strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", local_time);
            fprintf(fw, "%s [%s] %s\n", timestamp, get_debug_type(get_debug()), (char*)p->second);
            free(p->second);
        }else{
            Queue * q_file_log = (Queue*)p->second;
            while(!is_empty(q_file_log)){
                time_t now = time(NULL);
                struct tm *local_time = localtime(&now);
                char timestamp[30];
                strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", local_time);
                Pair *tp = (Pair*)queue_pop(q_file_log);
                fprintf(fw, "%s [%s] %s\n", timestamp, get_debug_type(get_debug()), (char*)tp->second);
                free(tp->first);
                free(tp->second);
                free(tp);
            } 
            queue_destroy(q_file_log);
        }
        fflush(fw);
        free(p->first);
        free(p);
        pthread_mutex_unlock(&log_mutex);
    }
    fclose(fw);
    queue_destroy(q_log);
    printf("log pthread exit...\n");
    return NULL;
}

/* initialize log pthread */
int init_log(){
    fw = fopen(LOGFILE, "w");
    if (fw == NULL) {
        perror("open log file failed");
        return 1;
    }
    q_log = queue_create("log");
    pthread_create(&log_thread, NULL, write_log, NULL);
    return 0;
}

/* free the log pthread */
void exit_log(){
    pthread_mutex_lock(&log_mutex);
    set_stop();
    pthread_cond_signal(&cond_full);
    pthread_mutex_unlock(&log_mutex);
    pthread_join(log_thread, NULL);
    pthread_mutex_destroy(&log_mutex);
    pthread_cond_destroy(&cond_full);
}

/* couting the elapse time of program */
void cost_time(struct timeval *start, struct timeval *end){
    char *time_unit;
    long *time_cost;
    long time_us = (end->tv_sec - start->tv_sec) * 1000000 + (end->tv_usec - start->tv_usec);
    long time_ms = time_us / 1000;
    long time_s = end->tv_sec - start->tv_sec;
    long time_min = time_s / 60;
    if(time_min > 0) time_cost = &time_min, time_unit = "min";
    else if(time_s > 0) time_cost = &time_s, time_unit = "s";
    else if(time_ms > 0) time_cost = &time_ms, time_unit = "ms";
    else time_cost = &time_us, time_unit = "us";
    _log(NONE, "----------------", "elapse time: %ld %s", *time_cost, time_unit);
    printf("elapse time: %ld %s\n", *time_cost, time_unit);
    exit_log();
}


